/**
 * 
 */
package com.vivid.dynamics.oknow.test.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.vivid.dynamics.junit.impl.SpringHibernateSupportJUnitTemplate;
import com.vivid.dynamics.oknow.config.spring.Constants;
import com.vivid.dynamics.oknow.dao.IGroupDAO;
import com.vivid.dynamics.oknow.dao.IUserDAO;
import com.vivid.dynamics.oknow.model.Group;
import com.vivid.dynamics.oknow.model.User;

/**
 *
 * @author jason
 * created at:: Aug 12, 2011 10:00:11 AM
 */
public class UserNGroupTestCase extends SpringHibernateSupportJUnitTemplate {

	/**
	 * IGroupDAO, check the config under config/spring-config.xml
	 */
	private @Autowired IGroupDAO groupDAO;
	/**
	 * IUserDAO, check the config under config/spring-config.xml
	 */
	private @Autowired IUserDAO userDAO;
	
	/**
	 * ctor.
	 */
	public UserNGroupTestCase() {
		super(UserNGroupTestCase.class, true);
	}
	
	User userA=null;
	User userB=null;
	Serializable userAKey=null;
	Serializable userBKey=null;
	
	Group grpA=null;
	Group grpB=null;
	Serializable grpAKey=null;
	Serializable grpBKey=null;
	
	/**
	 * 
	 * 
	 * @author jason
	 * created at:: Aug 12, 2011  11:17:59 AM
	 */
	protected void createDefaultEntities() {
		this.userA=new User("jonathan", "joestar", false, "RootAdmin->default", "abc@abc.com", null);
		this.userB=new User("joline", "joestar", false, "RootAdmin->default", "abc@abc.com", null);
		this.userAKey=this.userDAO.newUser(this.userA);
		this.userBKey=this.userDAO.newUser(this.userB);
		
		this.grpA=new Group("dummyGroup001", "for testing purpose, will be removed after test");
		this.grpB=new Group("dummyGroup002", "for testing purpose, will be removed after test");
		this.grpAKey=this.groupDAO.newGroup(grpA);
		this.grpBKey=this.groupDAO.newGroup(grpB);
		
		this.userA.addGroup(grpA);
		this.userB.addGroup(grpB);
		this.userDAO.updateUser(userA);
		this.userDAO.updateUser(userB);
	}
	
	/**
	 * <ul>
	 * 	<li>get the Group by an Id, provide 1 valid Id and 1 non-existing Id</li>
	 * 	<li>from a valid Group instance, try to get its User(s); this is mainly to validate the mapping on hibernate</li>
	 * </ul>
	 * 
	 * @author jason
	 * created at:: Aug 12, 2011  11:26:37 AM
	 */
	@Test
	public void doTest001() {
		Group aGrp001=null;
		Group aGrp002=null;
		
		this.sb.append("\r\n************************************************\r\n").
		    append("*\tstart {UserNGroupTestCase.doTest001}\t*\r\n").
			append("************************************************\r\n\r\n").
			append("calling method {doGetGroupTest}::\r\n");
		this.sb.append("1a) get the Group by an Id, provide 1 valid Id and 1 non-existing Id::\r\n\tExisting Id::").append(Constants.DEFAULT_GROUP).append("::"); 
		
		try {
			this.createDefaultEntities();
			
			aGrp001=this.groupDAO.getGroupByKey(Constants.DEFAULT_GROUP.intValue());
			Assert.assertNotNull("[Group CANT be null, id="+Constants.DEFAULT_GROUP+" is NEVER null~]", aGrp001);
			this.sb.append(aGrp001).append("\r\n\r\n");
			
			this.sb.append("1b) get the Group by an Id, provide 1 non-existing Id::\r\n\tnon-Existing Id::").append(-999).append("::");
			aGrp002=this.groupDAO.getGroupByKey(-999);
			Assert.assertNull("aGrp002 with id=-999 should be NULL", aGrp002);
			this.sb.append(aGrp002).append("\r\n\r\n");
			
			// 2nd test
			this.sb.append("2) from a valid Group instance, try to get its User(s); this is mainly to validate the mapping on hibernate:: \r\n\tUsers~ ");
			Assert.assertNotNull("[Group id="+Constants.DEFAULT_GROUP+" should HAVE at least 1 user~]", aGrp001.getUsers());
			Assert.assertTrue("[Group id="+Constants.DEFAULT_GROUP+" should HAVE at least 1 user~]", aGrp001.getUsers().size()>0);
			this.createToStringForCollections(this.sb, aGrp001.getUsers());
			this.sb.append("\r\n\r\n");
			
			this.tx.flush();
			
		} catch (RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			throw re;
		}
	}
	
	
	
	
	
	/**
	 * test on
	 * <ol>
	 * 	<li>get the Group by an Id, provide 1 valid Id and 1 non-existing Id</li>
	 * 	<li>from a valid Group instance, try to get its User(s); this is mainly to validate the mapping on hibernate</li>
	 * 	<li>from a valid Group instance, update the values of the group (should work as long as not related to the Users)</li>
	 * 	<li>from a valid Group instance, try to do updates based on Group (however owner is User, which should not have any modification)</li>
	 * 	<li>from a valid Group instance, try to do updates based on User (should work, provided that the NEW groupId is valid)</li>
	 * 	<li>simple delete and add on User, Group</li>
	 * 	<li>remove a group association from the user</li>
	 * </ol>
	 * <b>PS</b>. <strong>tx.flush</strong> is required since hibernate won't execute DML sqls when the transactions are rolled back
	 * however... in JUnit... u don't really need to do real commit DB operation<p/>
	 * @author jason
	 * created at:: Jul 28, 2011  2:55:56 PM
	 * @deprecated
	 */
	//@Test
	public void doUserNGroupTest001() {
		Serializable aKey=null;
		Group aGrp001=null;
		Group aGrp002=null;
		List<User> aUsrList001=null;
		List<Group> aGrpList001=null;
		User aUsr001=null;
		User aUsr002=null;
		
		// keys for delete
		List<Integer> aGrpKeyList=new ArrayList<Integer>();
		List<Integer> aUsrKeyList=new ArrayList<Integer>();
		
		this.sb.append("\r\n************************************************\r\n").
		    append("*\tstart {doUserNGroupTest001}\t*\r\n").
			append("************************************************\r\n\r\n").
			append("calling method {doGetGroupTest}::\r\n");
		this.sb.append("1a) get the Group by an Id, provide 1 valid Id and 1 non-existing Id::\r\n\tExisting Id::").append(Constants.DEFAULT_GROUP).append("::"); 
		
		try {
			aGrp001=this.groupDAO.getGroupByKey(Constants.DEFAULT_GROUP.intValue());
			Assert.assertNotNull("[Group CANT be null, id="+Constants.DEFAULT_GROUP+" is NEVER null~]", aGrp001);
			this.sb.append(aGrp001).append("\r\n\r\n");
			
			this.sb.append("1b) get the Group by an Id, provide 1 non-existing Id::\r\n\tnon-Existing Id::").append(-999).append("::");
			aGrp002=this.groupDAO.getGroupByKey(-999);
			this.sb.append(aGrp002).append("\r\n\r\n");
			
			// this.tx.flush();
			
			// 2nd test
			this.sb.append("2) from a valid Group instance, try to get its User(s); this is mainly to validate the mapping on hibernate:: \r\n\tUsers~ ");
			aUsrList001=aGrp001.getUsers();
			Assert.assertNotNull("[Group id="+Constants.DEFAULT_GROUP+" should HAVE at least 1 user~]", aUsrList001);
			Assert.assertTrue("[Group id="+Constants.DEFAULT_GROUP+" should HAVE at least 1 user~]", aUsrList001.size()>0);
			this.sb.append(aUsrList001).append("\r\n\r\n");
			
			// this.tx.flush();
			
			// 3rd test
			this.sb.append("3a) from a valid Group instance, update the values of the group (should work as long as not related to the Users):: \r\n\tCreate a new group (dummy and to be cleaned after test), \r\n\tKeys ::");
			aGrp002=new Group("dummyGroup", "for testing purpose, will be removed after test");
			aKey=this.groupDAO.newGroup(aGrp002);
			Assert.assertNotNull("[Key for the new Group Entity CANT be NULL~]", aKey);
			aGrpKeyList.add((Integer) aKey);
			this.sb.append(aKey).append("\r\n\r\n");
	
			// this.tx.flush();
			
			this.sb.append("3b) do updates, append a Timestamp to the desc of the Group \r\n\tSuccess? ::");
			aGrp002.setDescr(aGrp002.getDescr()+"~"+new Date());
			this.groupDAO.updateGroup(aGrp002);
			this.sb.append("true~ no Exception thrown!").append("\r\n\r\n");
			
			// this.tx.flush();
			
			this.sb.append("3c) do re-query, \r\n\tUpdate Group~ ::");
			aGrp002=this.groupDAO.getGroupByKey((Integer) aKey);
			this.sb.append(aGrp002).append("\r\n\r\n");
			
			// this.tx.flush();
			
			// 4th test
			this.sb.append("4a) from a valid Group instance, try to do updates based on Group (however owner is User, which should not have any modification)  \r\n\tAdding Users under a Group instance~ :: success? ");
			//aUsr001=new User("jonathan", "joestar", false, "RootAdmin->default", Constants.DEFAULT_USER_THEME.intValue(), "abc@abc.com", null);
			aUsr001=new User("jonathan", "joestar", false, "RootAdmin->default", "abc@abc.com", null);
			aGrp002.getUsers().add(aUsr001);
			this.sb.append(true).append("\r\n\r\n");
			
			// this.tx.flush();
			
			this.sb.append("4b) save the Group~ (however should not have changes)  \r\n\tupdate :: success? ");
			this.groupDAO.updateGroup(aGrp002);
			this.sb.append(true).append("\r\n\r\n");
			
			// this.tx.flush();
			
			this.sb.append("4c) re-query~ (however should not have changes) \r\n\tupdate :: Group :: ");
			aKey=aGrp002.getId();
			aGrp002=null;
			aUsrList001=null;
			aGrp002=this.groupDAO.getGroupByKey((Integer) aKey);
			Assert.assertNotNull("[Group Entity CANT be NULL @ 4c)~]", aGrp002);
			this.sb.append(aGrp002).append("\r\n\tThe User(s)~ :: ");
			aUsrList001=aGrp002.getUsers();
			Assert.assertNotNull("[User(s) should not be NULL @ 4c)~]", aUsrList001);
			Assert.assertTrue("[User(s) should not be empty @ 4c)~]", aUsrList001.size()>0);
			for (User aUsr:aUsrList001) {
				this.sb.append("\r\n\t").append(aUsr);
			}
			this.sb.append("\r\n\tPS. though it seems working, but note that the ID~ field is never NORMAL, it is always 0 which is AB-NORMAL\r\n\r\n");
			
			// this.tx.flush();
			
			// 5th test
			// from a valid Group instance, try to do updates based on User (should work, provided that the NEW groupId is valid)
			this.sb.append("5a) from a valid Group instance, try to do updates based on User (should work, provided that the NEW groupId is valid) \r\n\tcreate a User~ :: success? ");
			//aUsr002=new User("dummy", "user", false, "RootAdmin->default", Constants.DEFAULT_USER_THEME.intValue(), "abc@abc.com", null);
			aUsr002=new User("dummy", "user", false, "RootAdmin->default", "abc@abc.com", null);
			aKey=this.userDAO.newUser(aUsr002);
			aUsrKeyList.add((Integer) aKey);
			Assert.assertNotNull("[User(s) KEY MUST not be NULL @ 5a)~]", aKey);
			this.sb.append(true).append(", key :: ").append(aKey).append("\r\n\r\n");
			
			// this.tx.flush();
			
			this.sb.append("5b) create another dummy group WITH saving~ :: \r\n\tsuccess? true\r\n\r\n");
			aGrp002=new Group("dummy group 002", "to be deleted later on");
			aKey=this.groupDAO.newGroup(aGrp002);
			aGrpKeyList.add((Integer) aKey);
			aUsr002.addGroup(aGrp002);
			
			// this.tx.flush();
			
			this.sb.append("5c) User n Group association, set it to a dummy group again \r\n\tsave the OWNER (User) :: success? ");
			aKey=this.userDAO.newUser(aUsr002);
			aUsrKeyList.add((Integer) aKey);
			Assert.assertNotNull("[KEY should not be NULL @ 5c)~]", aKey);
			this.sb.append(true).append(", key::").append(aKey).append("\r\n\r\n");
			
			// this.tx.flush();
			
			this.sb.append("5d) re-query\r\n\tUser :: ");
			aUsr002=null;
			aUsr002=this.userDAO.getUserByKey((Integer) aKey);
			Assert.assertNotNull("[User should not be NULL @ 5d)~]", aUsr002);
			this.sb.append(aUsr002).append("\r\n");
			aGrpList001=aUsr002.getGroups();
			Assert.assertNotNull("[Group(s) should not be NULL @ 5d)~]", aGrpList001);
			Assert.assertTrue("[Group(s) should not be EMPTY @ 5d)~]", aGrpList001.size()>0);
			for (Group aGrp:aGrpList001) {
				this.sb.append("\r\n\t").append(aGrp);
			}
			this.sb.append("Key is updated for Group, which is NORMAL~~~~ comparing with 4c)\r\n").
				append("PS. note that before association, both Ends should be 'PERSISTED' first, at least the non-owner side should be a VALID entity~\r\n\r\n");
			
			// this.tx.flush();
			
			this.sb.append("5e) UPDATE (not NEW) the Group's desc, adding timestamp\r\n\tSave ok? :: ");
			aGrp002=aUsr002.getGroups().get(0);
			aGrp002.setDescr(aGrp002.getDescr()+" :: "+new Date());
			aUsr002.setBackupEmail("123@abc.com");
			this.userDAO.updateUser(aUsr002);
			this.sb.append(true).append("\r\n\r\n");
			
			// this.tx.flush();
			
			this.sb.append("5f) re-query\r\n\tUser :: ");
			aKey=aUsr002.getId();
			aUsr002=null;
			aGrpList001=null;
			aUsr002=this.userDAO.getUserByKey((Integer) aKey);
			Assert.assertNotNull("[User should not be NULL @ 5f)~]", aUsr002);
			this.sb.append(aUsr002).append("\r\n");
			aGrpList001=aUsr002.getGroups();
			Assert.assertNotNull("[Group(s) should not be NULL @ 5d)~]", aGrpList001);
			Assert.assertTrue("[Group(s) should not be EMPTY @ 5d)~]", aGrpList001.size()>0);
			for (Group aGrp:aGrpList001) {
				this.sb.append("\r\n\t").append(aGrp);
			}
			
			// this.tx.flush();
			
			// 6th test
			this.sb.append("\r\n\r\n6a) simple delete and add on User, Group \r\n\tdelete Usr::").append(aUsr002.getId()).append("=>");
			aKey=aUsr002.getId();
			aGrp002=aUsr002.getGroups().get(0);
			this.userDAO.deleteUser(aUsr002);
			aUsr002=this.userDAO.getUserByKey((Integer) aKey);
			Assert.assertNull("[this User should be removed~]", aUsr002);
			this.sb.append(aUsr002).append("\r\n\r\n");
			
			// this.tx.flush();
			
			this.sb.append("6b) delete Group ").append(aGrp001.getId()).append("=>");
			// will die if commented the following code			
			aGrp001=this.groupDAO.getAll().get(0);
			aKey=aGrp001.getId();
			this.groupDAO.deleteGroup(aGrp001);
			aGrp001=this.groupDAO.getGroupByKey((Integer) aKey);
			Assert.assertNull("[this Group should be removed~]", aGrp001);
			this.sb.append(aGrp001).append("\r\n\r\n");

			// this.tx.flush();
			
			// re-creation of a User linking up with dummy group
			//aUsr002=new User("dummy3", "", false, "RootAdmin->default", Constants.DEFAULT_USER_THEME.intValue(), "def@abc.com", null);
			aUsr002=new User("dummy3", "", false, "RootAdmin->default", "def@abc.com", null);
			// aGrp002 should be persistent; as it is created/retrieved by previous method
			aUsr002.addGroup(this.groupDAO.getAll().get(0));
			aUsrKeyList.add((Integer) this.userDAO.newUser(aUsr002));

			// this.tx.flush();
			
			// 7th test
			this.sb.append("7a) remove a group association from the user \r\n\tupdate~ :: success? ");
			aUsr002.removeGroup(aUsr002.getGroups().get(0));
			this.userDAO.updateUser(aUsr002);
			this.sb.append(true).append("\r\n\r\n");

			// this.tx.flush();
			
			this.sb.append("7b) re-query\r\n\tUser :: ");
			aKey=aUsr002.getId();
			aUsr002=null;
			aGrpList001=null;
			aUsr002=this.userDAO.getUserByKey((Integer) aKey);
			Assert.assertNotNull("[User should not be NULL @ 7b)~]", aUsr002);
			this.sb.append(aUsr002).append("\r\n");
			aGrpList001=aUsr002.getGroups();
			for (Group aGrp:aGrpList001) {
				this.sb.append("\r\n\t").append(aGrp);
			}

			this.tx.flush();
			

		} catch (RuntimeException re) {
			this.sb.append("**************************************\r\n").
				append(re.toString()).
				append("\r\n**************************************\r\n");
			re.printStackTrace(System.out);
			throw re;
			
		} finally {
			// clean up... no need as you've already setRollback = true + tx.flush() to force sqls to write (though rollback at the end)
			/*
			for (Integer aKy:aGrpKeyList) {
				this.groupDAO.deleteGroup(this.groupDAO.getGroupByKey(aKy));
				this.tx.flush();
			}
			for (Integer aKy:aUsrKeyList) {
				this.userDAO.deleteUser(this.userDAO.getUserByKey(aKy));
				this.tx.flush();
			}*/
		}
	}
}
